#include <linux/fs.h> 
#include <linux/init.h> 
#include <linux/module.h> 
#include <asm/uaccess.h> 
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/miscdevice.h>  

MODULE_LICENSE("GPL");
MODULE_VERSION( "3.0" ); 

#define EOK 0
 
static char *hello_str = "Hello, world!\n"; 
static int scull_minor = 0;
static int device_open = 0; 
  
static ssize_t scull_open(struct inode *n, struct file *f ) { 
   if( device_open ) return -EBUSY; 
   device_open++; 
   return EOK; 
}  

static ssize_t scull_read(struct file * file, char * buf, size_t count, loff_t *ppos ) { 
   	int len = strlen( hello_str ); 
  	 printk( KERN_INFO "=== read : %d\n", count ); 
   
	if( count < len ) return -EINVAL; 
   	if( *ppos != 0 ) { 
     		printk( KERN_INFO "=== read return : 0\n" );  // EOF 
      		return 0; 
   	} 
   	if( copy_to_user( buf, hello_str, len ) ) return -EINVAL; 
   	
	*ppos = len; 
	printk( KERN_INFO "=== read return : %d\n", len ); 
	return len; 
	} 

static ssize_t scull_release( struct inode *n, struct file *f ) { 
   device_open--; 
   return EOK; 
	}

static const struct file_operations scull_fops = {
    	.owner = THIS_MODULE, 
   	.open = scull_open, 
   	.release = scull_release, 
  	.read  = scull_read, 
}; 

static struct miscdevice misc_dev = { 
   MISC_DYNAMIC_MINOR,    // автоматически выбираемое 
   "scull", 
   &scull_fops 
}; 


static int __init scull_init( void ) {
	int ret; 

   	if( scull_minor != 0 ) misc_dev.minor = scull_minor; 
   	ret = misc_register( &misc_dev ); 
   	if( ret ) printk( KERN_ERR "=== Unable to register misc device\n" );
	else printk( KERN_INFO "=============== module installed =============\n" ); 
   	return ret; 
} 

  
static void __exit scull_exit( void ) {
	misc_deregister( &misc_dev );  
   	printk( KERN_INFO "=============== module removed =============\n" ); 
	}

module_init(scull_init);
module_exit(scull_exit);

